> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/jaypopat/cf_ai_duet/llms.txt
> Use this file to discover all available pages before exploring further.

# UI Package

> Bubble Tea TUI model for the Duet interface

## Overview

The `ui` package implements the Bubble Tea model for Duet's terminal user interface, managing screens, room interactions, terminal display, and AI sidebar.

## Types

### Model

Main UI model managing all application state.

```go theme={null}
type Model struct {
    screen   Screen
    width    int
    height   int
    username string
    clientID string
    
    selected int
    input    textinput.Model
    
    roomID       string
    currentRoom  *room.Room
    terminal     *terminal.Terminal
    termUpdateCh chan struct{}
    termContent  string
    users        []string
    toasts       []toast
    inputMode    InputMode
    cmdInput     textinput.Model
    typingUser   string
    typingTime   time.Time
    
    showAISidebar    bool
    aiViewport       viewport.Model
    aiLoading        bool
    aiSpinner        spinner.Model
    lastPromptOffset int
    
    eventChan chan room.RoomEvent
    
    roomManager *room.Manager
    aiClient    *ai.Client
    renderer    *lipgloss.Renderer
    styles      *Styles
}
```

### Screen

Enumeration of UI screens:

* `ScreenLaunch`: Main menu
* `ScreenCreate`: Create room form
* `ScreenJoin`: Join room form
* `ScreenRoomCreated`: Room created confirmation
* `ScreenRoom`: Active room with terminal

### InputMode

Terminal input modes:

* `ModeNormal`: Direct terminal input
* `ModeAI`: AI prompt input (Ctrl+G)
* `ModeSandbox`: Sandbox command input (Ctrl+R)

## Functions

### New

Creates a new UI model instance.

```go theme={null}
func New(renderer *lipgloss.Renderer, roomManager *room.Manager, username string) *Model
```

<ParamField path="renderer" type="*lipgloss.Renderer" required>
  Lipgloss renderer for styled output
</ParamField>

<ParamField path="roomManager" type="*room.Manager" required>
  Room manager instance
</ParamField>

<ParamField path="username" type="string" required>
  SSH username (defaults to "guest" if empty)
</ParamField>

**Returns:** Initialized `*Model` ready for Bubble Tea

**Example:**

```go theme={null}
renderer := bubbletea.MakeRenderer(sess)
model := ui.New(renderer, roomManager, sess.User())
```

### Init

Bubble Tea initialization command.

```go theme={null}
func (m *Model) Init() tea.Cmd
```

**Returns:** Tick command for toast expiration

### Update

Bubble Tea update function handling messages.

```go theme={null}
func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd)
```

**Key Messages:**

* `tea.WindowSizeMsg`: Resize terminal and viewports
* `tea.KeyMsg`: Keyboard input routing
* `terminalUpdateMsg`: Terminal content changed
* `roomEventMsg`: Room events (join/leave/typing/ai\_sync)
* `AIResponseMsg`: AI response received
* `SandboxResultMsg`: Sandbox command completed

### View

Bubble Tea view function rendering the UI.

```go theme={null}
func (m *Model) View() string
```

**Returns:** Rendered screen as string

## Message Types

### GotoScreenMsg

Navigate to a different screen.

```go theme={null}
type GotoScreenMsg struct {
    Screen Screen
}
```

### RoomCreatedMsg

Room successfully created.

```go theme={null}
type RoomCreatedMsg struct {
    RoomID string
    Room   *room.Room
}
```

### RoomJoinedMsg

Successfully joined a room.

```go theme={null}
type RoomJoinedMsg struct {
    RoomID string
    Room   *room.Room
}
```

### AIResponseMsg

AI response received.

```go theme={null}
type AIResponseMsg struct {
    Reply    string
    Messages []AIMessage
}
```

### SandboxResultMsg

Sandbox command output.

```go theme={null}
type SandboxResultMsg struct {
    Output string
    Cmd    string
}
```

### ToastMsg

Display a toast notification.

```go theme={null}
type ToastMsg struct {
    Text string
}
```

### ErrorMsg

Error occurred.

```go theme={null}
type ErrorMsg struct {
    Err error
}
```

## Key Bindings

### Launch Screen

* `c` / `C`: Create room
* `J`: Join room
* `↑` / `k`: Move selection up
* `↓` / `j`: Move selection down
* `Enter`: Confirm selection
* `q` / `Esc`: Quit

### Room Screen (Normal Mode)

* `Ctrl+G`: Open AI prompt
* `Ctrl+R`: Open sandbox command
* `Ctrl+A`: Toggle AI sidebar
* `Ctrl+J`: Scroll AI sidebar down
* `Ctrl+K`: Scroll AI sidebar up
* `Ctrl+L`: Leave room
* All other keys: Sent to terminal

### AI/Sandbox Input Mode

* `Enter`: Submit prompt/command
* `Esc`: Cancel and return to normal mode

## Layout Configuration

```go theme={null}
const (
    MinWidthForSidebar  = 120
    MinHeightForSidebar = 24
)
```

Sidebar visibility and layout adapt to terminal size.

## Internal Methods

### roomLayout

Calculates layout dimensions.

```go theme={null}
func (m *Model) roomLayout() (sidebarW, terminalW, aiSidebarW, mainH int)
```

**Returns:**

* `sidebarW`: User list width (1/6 of screen)
* `terminalW`: Terminal width (remaining space)
* `aiSidebarW`: AI sidebar width (1/4 if visible, 0 otherwise)
* `mainH`: Main content height

### startTerminal

Initializes or connects to room terminal.

```go theme={null}
func (m *Model) startTerminal() tea.Cmd
```

**Behavior:**

* Reuses existing room terminal if available
* Creates new terminal with room workspace directory
* Subscribes to terminal updates
* Stores terminal reference in room for other clients

### sendAIMessage

Sends AI prompt to worker.

```go theme={null}
func (m *Model) sendAIMessage(text string) tea.Cmd
```

<ParamField path="text" type="string" required>
  User prompt
</ParamField>

**Returns:** Command that yields `AIResponseMsg` or `ErrorMsg`

### execSandboxCmd

Executes command in isolated sandbox.

```go theme={null}
func (m *Model) execSandboxCmd(cmd string) tea.Cmd
```

<ParamField path="cmd" type="string" required>
  Shell command to execute
</ParamField>

**Returns:** Command that yields `SandboxResultMsg` or `ErrorMsg`

### syncAIViewportContent

Rebuild AI viewport from room's message history.

```go theme={null}
func (m *Model) syncAIViewportContent()
```

**Use Cases:**

* Late joiners syncing chat history
* Refreshing after another client sends AI message
* Responding to "ai\_sync" room events

### addToast

Displays a temporary notification.

```go theme={null}
func (m *Model) addToast(text string)
```

<ParamField path="text" type="string" required>
  Toast message (visible for 1 second)
</ParamField>

**Limit:** Maximum 3 toasts displayed at once

### cleanup

Cleans up resources when leaving room.

```go theme={null}
func (m *Model) cleanup()
```

**Cleanup:**

* Unsubscribes from terminal updates
* Leaves room via manager
* Clears terminal reference
* Resets room state

## Example Usage

```go theme={null}
// In SSH session handler
func teaHandler(sess ssh.Session) (tea.Model, []tea.ProgramOption) {
    username := sess.User()
    if username == "" {
        username = "guest"
    }
    
    renderer := bubbletea.MakeRenderer(sess)
    model := ui.New(renderer, roomManager, username)
    
    return model, []tea.ProgramOption{
        tea.WithAltScreen(),
    }
}
```
